﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace BCCServer.Communication.SocketServer
{
    public class server
    {
        Socket socketsevice;
        //List<Socket> userlist;//用户组
        static Dictionary<string, Socket> userlist;


        public server()
        {
            socketsevice = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            userlist = new Dictionary<string, Socket>();
        }

        #region 拆包

        public static void AcceptClient(Socket server)
        {
            Socket client = null;
            do
            {
                try
                {
                    client = server.Accept();
                    if (client != null)
                    {
                        Console.WriteLine("客户 \'{0}\' 已连接。", client.RemoteEndPoint.ToString());
                        Task.Run(() => ReceiveData(client)).Wait(100);
                    }
                }
                catch (Exception ex)
                {
                    client = null;
                }
            } while (client != null);
        }

        public static void ReceiveData(Socket socket)  // 接收消息函数(传入一个socket对象)
        {
            try
            {
                MessageProtocol mp = null;
                int ReceiveLength = 0;
                byte[] staticReceiveBuffer = new byte[65536];  // 接收缓冲区(固定长度)
                byte[] dynamicReceiveBuffer = new byte[] { };  // 累加数据缓存(不定长)
                do
                {
                    ReceiveLength = socket.Receive(staticReceiveBuffer);  // 同步接收数据
                    dynamicReceiveBuffer = CombineBytes(dynamicReceiveBuffer, 0, dynamicReceiveBuffer.Length, staticReceiveBuffer, 0, ReceiveLength);  // 将之前多余的数据与接收的数据合并,形成一个完整的数据包
                    if (ReceiveLength <= 0)  // 如果接收到的数据长度小于0(通常表示socket已断开,但也不一定,需要进一步判断,此处可以忽略)
                    {
                        break;  // 终止接收循环
                    }
                    else if (dynamicReceiveBuffer.Length < MessageProtocol.HEADLENGTH)  // 如果缓存中的数据长度小于协议头长度,则继续接收
                    {
                        continue;  // 跳过本次循环继续接收数据
                    }
                    else  // 缓存中的数据大于等于协议头的长度(dynamicReadBuffer.Length >= 6)
                    {
                        var headInfo = MessageProtocol.GetHeadInfo(dynamicReceiveBuffer);  // 解读协议头的信息
                        while (dynamicReceiveBuffer.Length - MessageProtocol.HEADLENGTH >= headInfo.DataLength)  // 当缓存数据长度减去协议头长度大于等于实际数据的长度则进入循环进行拆包处理
                        {
                            mp = new MessageProtocol(dynamicReceiveBuffer);  // 拆包
                            dynamicReceiveBuffer = mp.MoreData;  // 将拆包后得出多余的字节付给缓存变量,以待下一次循环处理数据时使用,若下一次循环缓存数据长度不能构成一个完整的数据包则不进入循环跳到外层循环继续接收数据并将本次得出的多余数据与之合并重新拆包,依次循环。
                            headInfo = MessageProtocol.GetHeadInfo(dynamicReceiveBuffer);  // 从缓存中解读出下一次数据所需要的协议头信息,已准备下一次拆包循环,如果数据长度不能构成协议头所需的长度,拆包结果为0,下一次循环则不能成功进入,跳到外层循环继续接收数据合并缓存形成一个完整的数据包
                                                                                           //if (mp.Command == 0 && mp.Param == 1)
                                                                                           //{
                                                                                           //    socket.Send(new MessageProtocol(0, 0, Encoding.UTF8.GetBytes("Goodbye\r\n再见!")).GetBytes());
                                                                                           //    return;
                                                                                           //}

                            ResponseRequest(socket, mp.GetMessage());
                        } // 拆包循环结束
                    }
                } while (ReceiveLength > 0);
            }
            finally
            {
                Console.WriteLine("客户 \'{0}\' 已离开", socket.RemoteEndPoint.ToString());
                socket.Close();
            }
        }

        public static void ResponseRequest(Socket client, (byte Command, byte Param, int DataLength, byte[] MessageData) message)
        {
            
            if (message.Command == 0 && message.Param == 1)//退出
            {
                string text = Encoding.UTF8.GetString(message.MessageData);
                //client.Send(new MessageProtocol(0, 1, Encoding.UTF8.GetBytes("Goodbye\r\n再见!")).GetBytes());
                userlist.Remove(text);
                return;
            }
            else if (message.Command == 0 && message.Param == 0)//登录
            {
                string text = Encoding.UTF8.GetString(message.MessageData);
                try
                {
                    if (userlist.ContainsKey(text))
                    {
                        userlist.Remove(text);
                    }
                    userlist.Add(text, client);
                }
                catch (Exception)
                {
                }
                
            }
            else if (message.Command == 1 && message.Param == 0)//一对一发送消息
            {
                string text = Encoding.UTF8.GetString(message.MessageData);
                broadcast(text);
            }
            else if (message.Command == 1 && message.Param == 1)//一对一发送消息发送图片
            {
                //string text = Encoding.UTF8.GetString(message.MessageData);
                //Console.WriteLine("客户 \'{0}\' 发来信息: \'{1}\'。", client.RemoteEndPoint.ToString(), text);
                broadcastPic(message.MessageData);
            }
            //client.Send(new MessageProtocol(1, 1, Encoding.UTF8.GetBytes(text)).GetBytes());
        }

        public static byte[] CombineBytes(byte[] firstBytes, int firstIndex, int firstLength, byte[] secondBytes, int secondIndex, int secondLength)
        {
            byte[] bytes = null;
            MemoryStream ms = new MemoryStream();
            ms.Write(firstBytes, firstIndex, firstLength);
            ms.Write(secondBytes, secondIndex, secondLength);
            bytes = ms.ToArray();
            ms.Close();
            return (bytes);
        }

        #endregion


        public void start()
        {
            socketsevice.Bind(new IPEndPoint(IPAddress.Any, 5566));
            socketsevice.Listen(100000);
            Console.WriteLine("服务器启动成功");
            Task.Run(() => AcceptClient(socketsevice)).Wait(10);
            //Console.WriteLine("服务器以启动,IP:\'{0}\',正在等待客户端连接 ...\r\n按 Enter 键退出 ->", socketsevice.LocalEndPoint.ToString());
            //Console.ReadLine();
            //socketsevice.Close();
            //Console.WriteLine("服务器已停止运行");


            ////开启接受连接,用多线程
            //Thread accthread = new Thread(accept);
            //accthread.IsBackground = true;
            //accthread.Start();
        }

        #region 
        //private void accept()
        //{
        //    //接受连接
        //    Socket clientsocket = socketsevice.Accept();
        //    userlist.Add(clientsocket);
        //    //打印已经连接ip地址
        //    Console.WriteLine(iptoaddress(clientsocket) + "连接进来了");

        //    //
        //    Thread recvthread = new Thread(recemessage);
        //    recvthread.IsBackground = true;
        //    recvthread.Start(clientsocket);

        //    accept();//递归
        //}
        ////接收客户端信息
        //private void recemessage(object obj)
        //{
        //    Socket client = obj as Socket;
        //    byte[] strbyte = new byte[1024 * 1024];//设定接受字符的长度
        //    string str = "";
        //    try
        //    {
        //        int len = client.Receive(strbyte);//接受用户发送的内容
        //        str = Encoding.Default.GetString(strbyte, 0, len);
        //        broadcast(str, client);//广播给用户
        //        Console.WriteLine(str);
        //    }
        //    catch (Exception e)
        //    {
        //        Console.WriteLine(iptoaddress(client) + "退出");
        //        userlist.Remove(client);
        //        Thread.CurrentThread.Abort();//退出时掐死线程，不然递归反弹
        //    }
        //    recemessage(client); //使用递归
        //}

        ///// <summary>
        ///// 广播信息
        ///// </summary>
        ///// <param name="usestr">传入收到的传输的内容</param>
        ///// <param name="obj">传送信息的客户</param>
        //private void broadcast(string userstr, object obj)
        //{
        //    Socket clientsend = obj as Socket; //当前发送信息的客户
        //    foreach (Socket client in userlist.Keys)
        //    {
        //        if (client != clientsend)//将信息广播给其他用户
        //        {
        //            client.Send(Encoding.Default.GetBytes(iptoaddress(clientsend) + ":" + userstr));
        //        }
        //    }
        //}
        #endregion

        /// <summary>
        /// 广播信息
        /// </summary>
        /// <param name="usestr">传入收到的传输的内容</param>
        private static void broadcast(string userstr)
        {
            //Socket clientsend = obj as Socket; //当前发送信息的客户
            var mes = userstr.Remove(0, userstr.IndexOf(";") + 1);
            var 账号 = userstr.Substring(0, userstr.IndexOf(";"));
            foreach (string client in userlist.Keys)
            {
                if (账号 == client)
                {
                    userlist[client].Send(new MessageProtocol(1, 0, Encoding.UTF8.GetBytes(mes)).GetBytes());
                }
            }
        }

        /// <summary>
        /// 广播信息（图片）
        /// </summary>
        /// <param name="usestr">传入收到的传输的内容</param>
        private static void broadcastPic(byte[] MessageData)
        {
            var 账号 = BitConverter.ToInt32(MessageData, 0).ToString();


            //Socket clientsend = obj as Socket; //当前发送信息的客户
            //var mes = userstr.Remove(0, userstr.IndexOf(";") + 1);
            //var 账号 = userstr.Substring(0, userstr.IndexOf(";"));
            byte[] imageBytes = new byte[MessageData.Length - sizeof(int)];
            Buffer.BlockCopy(MessageData, sizeof(int), imageBytes, 0, imageBytes.Length);

            foreach (string client in userlist.Keys)
            {
                if (账号 == client)
                {
                    userlist[client].Send(new MessageProtocol(1, 0, imageBytes).GetBytes());
                }
            }
        }

        //转换出连来客户的ip地址
        private string iptoaddress(Socket soket)
        {
            return (soket.RemoteEndPoint as IPEndPoint).Address.ToString();
        }
    }
}
